package com.sk.orm;

import com.sk.orm.support.Criteria;
import com.sk.orm.support.CriteriaSupport;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import jakarta.persistence.TypedQuery;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * CommonDao抽象实现
 *
 * @author smy
 * {@code @date} 2022/10/26
 */
public abstract class CommonDaoAbstract implements CommonDao {
    protected abstract EntityManager getEntityManager();

    @Override
    public <T> T add(T t) {
        getEntityManager().persist(t);
        return t;
    }

    @Override
    public <T> T update(T t) {
        return getEntityManager().merge(t);
    }

    @Override
    public void delete(Object o) {
        getEntityManager().remove(o);
    }

    @Override
    public <T> T find(Class<T> e, Object id) {
        return getEntityManager().find(e, id);
    }

    private <T> long count(CriteriaSupport<T> cd) {
        String jsql = "select count(1) " + cd.getFrom() + cd.getWhere() + cd.getGroupBy() + cd.getHaving();
        TypedQuery<Long> query = getEntityManager().createQuery(jsql, Long.class);
        cd.getParamMap().forEach(query::setParameter);
        return query.getSingleResult();
    }

    private <T> List<T> list(CriteriaSupport<T> cd) {
        EntityManager entityManager = getEntityManager();
        String jsql = cd.getSelect() + cd.getFrom() + cd.getWhere() + cd.getGroupBy() + cd.getHaving() + cd.getOrderBy();
        Query query = entityManager.createQuery(jsql);
        cd.getParamMap().forEach(query::setParameter);
        if (cd.getOffset() != null) {
            query.setFirstResult(cd.getOffset());
        }
        if (cd.getSize() != null) {
            query.setMaxResults(cd.getSize());
        }
        List<?> list = query.getResultList();
        return list.stream().map(cd.getVf()).collect(Collectors.toList());
    }

    @Override
    public <T> List<T> list(Criteria<T> c) {
        return list(new CriteriaSupport<>(c));
    }


    @Override
    public <T> long count(Criteria<T> c) {
        return count(new CriteriaSupport(c));
    }

    @Override
    public <T> PageResult<T> page(Criteria<T> c) {
        CriteriaSupport<T> cj = new CriteriaSupport<>(c);
        return new PageResult<>(count(cj), list(cj));
    }

    @Override
    public int execute(String jsql, Map<String, Object> param) {
        Query query = getEntityManager().createQuery(jsql);
        param.forEach(query::setParameter);
        return query.executeUpdate();
    }

    @Override
    public <T> List<T> select(String jsql, Map<String, Object> param, Function<Object, T> function) {
        Query query = getEntityManager().createQuery(jsql);
        param.forEach(query::setParameter);
        List<?> list = query.getResultList();
        return list.stream().map(function).collect(Collectors.toList());
    }


    @Override
    public int executeSql(String sql, Map<String, Object> param) {
        Query query = getEntityManager().createNamedQuery(sql);
        param.forEach(query::setParameter);
        return query.executeUpdate();
    }

    @Override
    public <T> List<T> selectSql(String sql, Map<String, Object> param, Function<Object, T> function) {
        Query query = getEntityManager().createNamedQuery(sql);
        param.forEach(query::setParameter);
        List<?> list = query.getResultList();
        return list.stream().map(function).collect(Collectors.toList());
    }
}
